# -*- coding:utf-8 -*-

'''
one embedding测试
在GTX960上，36s一轮
经过30轮迭代，训练集准确率为95.95%，测试集准确率为89.55%
Dropout不能用太多，否则信息损失太严重
'''


import numpy as np
import pandas as pd

pos = pd.read_excel('pos.xls', header=None)
pos['label'] = 1
neg = pd.read_excel('neg.xls', header=None)
neg['label'] = 0
all_ = pos.append(neg, ignore_index=True)

maxlen = 200 # 截断字数
min_count = 20 # 出现次数少于该值的字扔掉。这是最简单的降维方法

content = ''.join(all_[0])   # 合并文本数据
abc = pd.Series(list(content)).value_counts()  # series是排序，value_counts是统计字频
abc = abc[abc >= min_count] # 根据min_count筛选
abc[:] = range(1, len(abc)+1) # 按频数排序并跟上编号
abc[''] = 0 # 添加空字符串用来补全，在编号列的结尾多一个零
word_set = set(abc.index) # 变成列表样式的集合

def doc2num(s, maxlen):  # s应该是单个句子，这一步估计可以用FeatureHasher完成
    s = [i for i in s if i in word_set]  #对于任意一个句子，检查每个字符是否在word_set里，是的话拿出来
    s = s[:maxlen] + ['']*max(0, maxlen-len(s)) #
    return list(abc[s])

all_['doc2num'] = all_[0].apply(lambda s: doc2num(s, maxlen))  # 将每个句子按照doc2num的方式转为定长数字序列并存在all_的doc2num名下

#手动打乱数据
idx = range(len(all_)) # 生成一个列表
np.random.shuffle(idx) # 打乱列表排序
all_ = all_.loc[idx]  # 根据排序列表重排原始数据

# 按keras的输入要求来生成数据
x = np.array(list(all_['doc2num']))
y = np.array(list(all_['label']))
y = y.reshape((-1,1)) # 调整标签形状，从1×N变为Nx1的形状


from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout, Embedding
from keras.layers import LSTM

#建立模型
model = Sequential()
model.add(Embedding(len(abc), 256, input_length=maxlen))  # input_dim是字典长度、输入数据的最大下标+1,output_dim是全连接嵌入的维度，
# input_length：当输入序列的长度固定时，该值为其长度。如果要在该层后接Flatten层，然后接Dense层，则必须指定该参数，否则Dense层的输出维度无法自动推断。
model.add(LSTM(128))   # 输出维度
model.add(Dropout(0.5)) # 为输入数据施加Dropout。Dropout将在训练过程中每次更新参数时随机断开一定百分比（rate）的输入神经元，
# Dropout层用于防止过拟合。rate：0~1的浮点数，控制需要断开的神经元的比例
model.add(Dense(1))  # Dense就是常用的全连接层，所实现的运算是output = activation(dot(input, kernel)+bias)。其中activation是逐元素计算的
# 激活函数，kernel````是本层的权值矩阵，bias为偏置向量，只有当use_bias=True```才会添加。units：大于0的整数，代表该层的输出维度。
model.add(Activation('sigmoid')) # 激活层对一个层的输出施加激活函数
# activation：将要使用的激活函数，为预定义激活函数名或一个Tensorflow/Theano的函数
model.compile(loss='binary_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

batch_size = 128
train_num = 15000

model.fit(x[:train_num], y[:train_num], batch_size = batch_size, nb_epoch=30)  # fit的是整个数据集的前部分，不包括train_num行和以后的，
# batch是每一批的数据量

model.evaluate(x[train_num:], y[train_num:], batch_size = batch_size) # 指标评估

def predict_one(s): # 单个句子的预测函数
    s = np.array(doc2num(s, maxlen))
    s = s.reshape((1, s.shape[0]))
    return model.predict_classes(s, verbose=0)[0][0]